home *** CD-ROM | disk | FTP | other *** search
/ Meeting Pearls 1 / Meeting Pearls Vol 1 (1994).iso / installed_progs / gfx / pbm / source / sgitopnm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-14  |  12.3 KB  |  470 lines

  1. /* sgitopnm.c - read an SGI image and and produce a portable anymap
  2. **
  3. ** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
  4. **
  5. ** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp)
  6. ** Available via ftp from sgi.com:graphics/SGIIMAGESPEC
  7. **
  8. ** Permission to use, copy, modify, and distribute this software and its
  9. ** documentation for any purpose and without fee is hereby granted, provided
  10. ** that the above copyright notice appear in all copies and that both that
  11. ** copyright notice and this permission notice appear in supporting
  12. ** documentation.  This software is provided "as is" without express or
  13. ** implied warranty.
  14. **
  15. ** 29Jan94: first version
  16. ** 08Feb94: minor bugfix
  17. ** 12Jun94: avoid fseek() if possible, fix for images with >3 channels
  18. */
  19. #include "pnm.h"
  20. #include "sgi.h"
  21.  
  22. /*#define DEBUG*/
  23.  
  24. #ifndef SEEK_SET
  25. #define SEEK_SET    0
  26. #endif
  27.  
  28.  
  29. /* entry in RLE offset table */
  30. typedef struct {
  31.     long start;     /* offset in file */
  32.     long length;    /* length of compressed scanline */
  33. } TabEntry;
  34.  
  35. typedef short       ScanElem;
  36. typedef ScanElem *  ScanLine;
  37.  
  38. /* prototypes */
  39. static unsigned char get_byte ARGS(( FILE* f ));
  40. static long get_big_long ARGS((FILE *f));
  41. static short get_big_short ARGS((FILE *f));
  42. static short get_byte_as_short ARGS((FILE *f));
  43. static void readerr ARGS((FILE *f));
  44. static void * xmalloc ARGS((int bytes));
  45. #define MALLOC(n, type)     (type *)xmalloc((n) * sizeof(type))
  46. static char * compression_name ARGS((char compr));
  47. static void       read_bytes ARGS((FILE *ifp, int n, char *buf));
  48. static Header *   read_header ARGS((FILE *ifp));
  49. static TabEntry * read_table ARGS((FILE *ifp, int tablen));
  50. static ScanLine * read_channels ARGS((FILE *ifp, Header *head, TabEntry *table, short (*func) ARGS((FILE *)) ));
  51. static void       image_to_pnm ARGS((Header *head, ScanLine *image, xelval maxval));
  52. static void       rle_decompress ARGS((ScanElem *src, int srclen, ScanElem *dest, int destlen));
  53.  
  54. #define WORSTCOMPR(x)   (2*(x) + 2)
  55.  
  56.  
  57. static short verbose = 0;
  58.  
  59.  
  60. int
  61. main(argc, argv)
  62.     int argc;
  63.     char *argv[];
  64. {
  65.     FILE *ifp;
  66.     int argn;
  67.     char *usage = "[-verbose] [sgifile]";
  68.     TabEntry *table = NULL;
  69.     ScanLine *image;
  70.     Header *head;
  71.     long maxval;
  72.  
  73.     pnm_init(&argc, argv);
  74.  
  75.     argn = 1;
  76.     while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
  77.         if( pm_keymatch(argv[argn], "-verbose", 2) )
  78.             verbose++;
  79.         else
  80.         if( pm_keymatch(argv[argn], "-noverbose", 4) )
  81.             verbose = 0;
  82.         else
  83.             pm_usage(usage);
  84.         ++argn;
  85.     }
  86.  
  87.     if( argn < argc ) {
  88.         ifp = pm_openr( argv[argn] );
  89.         argn++;
  90.     }
  91.     else
  92.         ifp = stdin;
  93.  
  94.     if( argn != argc )
  95.         pm_usage(usage);
  96.  
  97.     head = read_header(ifp);
  98.     maxval = head->pixmax - head->pixmin;
  99.     if( maxval > PNM_MAXMAXVAL )
  100.         pm_error("pixel values too large - try reconfiguring with PGM_BIGGRAYS\n    or without PPM_PACKCOLORS");
  101.  
  102.     if( head->storage != STORAGE_VERBATIM )
  103.         table = read_table(ifp, head->ysize * head->zsize);
  104.     if( head->bpc == 1 )
  105.         image = read_channels(ifp, head, table, get_byte_as_short);
  106.     else
  107.         image = read_channels(ifp, head, table, get_big_short);
  108.  
  109.     image_to_pnm(head, image, (xelval)maxval);
  110.     pm_close(ifp);
  111.  
  112.     exit(0);
  113. }
  114.  
  115.  
  116. static Header *
  117. read_header(ifp)
  118.     FILE *ifp;
  119. {
  120.     Header *head;
  121.  
  122.     head = MALLOC(1, Header);
  123.  
  124.     head->magic     = get_big_short(ifp);
  125.     head->storage   = get_byte(ifp);
  126.     head->bpc       = get_byte(ifp);
  127.     head->dimension = get_big_short(ifp);
  128.     head->xsize     = get_big_short(ifp);
  129.     head->ysize     = get_big_short(ifp);
  130.     head->zsize     = get_big_short(ifp);
  131.     head->pixmin    = get_big_long(ifp);
  132.     head->pixmax    = get_big_long(ifp);
  133.     read_bytes(ifp, 4, head->dummy1);
  134.     read_bytes(ifp, 80, head->name);
  135.     head->colormap  = get_big_long(ifp);
  136.     read_bytes(ifp, 404, head->dummy2);
  137.  
  138.     if( head->magic != SGI_MAGIC )
  139.         pm_error("bad magic number - not an SGI image");
  140.     if( head->storage < 0 || head->storage > 1 )
  141.         pm_error("unknown compression type");
  142.     if( head->bpc < 1 || head->bpc > 2 )
  143.         pm_error("illegal precision value %d (only 1-2 allowed)", head->bpc );
  144.     if( head->colormap != CMAP_NORMAL )
  145.         pm_error("unsupported non-normal pixel data");
  146.     if( head->zsize == 0 )
  147.         pm_error("illegal channel (zsize) value 0");
  148.  
  149.     if( verbose ) {
  150.         pm_message("raster size %dx%d, %d channels", head->xsize, head->ysize, head->zsize);
  151.         pm_message("compression: %d = %s", head->storage, compression_name(head->storage));
  152.         head->name[79] = '\0';  /* just to be safe */
  153.         pm_message("Image name: \"%s\"", head->name);
  154. #ifdef DEBUG
  155.         pm_message("bpc: %d    dimension: %d", head->bpc, head->dimension);
  156.         pm_message("pixmin: %ld    pixmax: %ld    colormap: %ld", head->pixmin, head->pixmax, head->colormap);
  157. #endif
  158.     }
  159.  
  160.     /* check ysize/zsize & dimension, just to be sure */
  161. again:
  162.     switch( head->dimension ) {
  163.         case 1:
  164.             if( head->zsize != 1 ) {
  165.                 pm_message("1-dimensional image with %d channels ?!", head->zsize);
  166.                 pm_message("trying to interpret this as a 3-dimensional image...");
  167.                 head->dimension = 3;
  168.                 goto again;
  169.             }
  170.             if( head->ysize == 1 )
  171.                 break; /* ok */
  172.             pm_message("1-dimensional image with ysize %d ?!", head->ysize);
  173.             pm_message("trying to interpret this as a 2-dimensional image...");
  174.             head->dimension = 2;
  175.             /* fall through */
  176.         case 2:
  177.             if( head->zsize == 1 )
  178.                 break; /* ok */
  179.             pm_message("2-dimensional image with %d channels ?!", head->zsize);
  180.             pm_message("trying to interpret this as a 3-dimensional image...");
  181.             head->dimension = 3;
  182.             /* fall through */
  183.         case 3:
  184.             switch( head->zsize ) {
  185.                 case 1:
  186.                 case 2:
  187.                     pm_message("3-dimensional image with %d channels ?!", head->zsize);
  188.                     pm_message("interpreting this image as greyscale");
  189.                     /*head->dimension = 2;*/
  190.                     break;
  191.                 case 3:
  192.                     break;
  193.                 default:
  194.                     pm_message("%d-channel image, using only first 3 channels", head->zsize);
  195.                     /*head->zsize = 3;*/
  196.                     break;
  197.             }
  198.             break;
  199.         default:
  200.             pm_error("illegal dimension value %d (only 1-3 allowed)", head->dimension);
  201.     }
  202.  
  203.     return head;
  204. }
  205.  
  206.  
  207. static TabEntry *
  208. read_table(ifp, tablen)
  209.     FILE *ifp;
  210.     int tablen;
  211. {
  212.     TabEntry *table;
  213.     int i;
  214.  
  215.     table = MALLOC(tablen, TabEntry);
  216.  
  217. #ifdef DEBUG
  218.     pm_message("reading offset table");
  219. #endif
  220.  
  221.     for( i = 0; i < tablen; i++ )
  222.         table[i].start = get_big_long(ifp);
  223.     for( i = 0; i < tablen; i++ )
  224.         table[i].length = get_big_long(ifp);
  225.  
  226.     return table;
  227. }
  228.  
  229.  
  230.  
  231. static ScanLine *
  232. read_channels(ifp, head, table, func)
  233.     FILE *ifp;
  234.     Header *head;
  235.     TabEntry *table;
  236.     short (*func) ARGS((FILE *));
  237. {
  238.     ScanLine *image;
  239.     ScanElem *temp;
  240.     int channel, maxchannel, row, sgi_index, i;
  241.     long offset, length;
  242.  
  243. #ifdef DEBUG
  244.     pm_message("reading channels");
  245. #endif
  246.  
  247.     maxchannel = head->zsize;
  248.     if( maxchannel < 3 )
  249.         maxchannel = 1;
  250.     else if( maxchannel > 3 )
  251.         maxchannel = 3;
  252.     image = MALLOC(head->ysize * maxchannel, ScanLine);
  253.     if( table ) temp = MALLOC(WORSTCOMPR(head->xsize), ScanElem);
  254.  
  255.     for( channel = 0; channel < maxchannel;  channel++ ) {
  256. #ifdef DEBUG
  257.         pm_message("    channel %d", channel);
  258. #endif
  259.         for( row = 0; row < head->ysize; row++ ) {
  260.             sgi_index = channel * head->ysize + row;
  261.             image[sgi_index] = MALLOC(head->xsize, ScanElem);
  262.             if( table ) {
  263.                 offset = table[sgi_index].start;
  264.                 length = table[sgi_index].length;
  265.                 if( head->bpc == 2 )
  266.                     length /= 2;   /* length is in bytes, we are reading words */
  267.                 if( length > WORSTCOMPR(head->xsize) )
  268.                     pm_error("corrupted start/length table for channel %d scanline %d ??", channel, row);
  269.                 if( ftell(ifp) != offset ) {  /* avoid unnescessary fseek() */
  270.                     if( fseek(ifp, offset, SEEK_SET) != 0 )
  271.                         pm_error("seek error for offset %ld - channel %d scanline %d", offset, channel, row);
  272.                 }
  273.                 for( i = 0; i < length; i++ )
  274.                     temp[i] = (*func)(ifp);
  275.                 rle_decompress(temp, length, image[sgi_index], head->xsize);
  276.             }
  277.             else {
  278.                 for( i = 0; i < head->xsize; i++ )
  279.                     image[sgi_index][i] = (*func)(ifp);
  280.             }
  281.         }
  282.     }
  283.  
  284.     if( table ) free(temp);
  285.     return image;
  286. }
  287.  
  288.  
  289. static void
  290. image_to_pnm(head, image, maxval)
  291.     Header *head;
  292.     ScanLine *image;
  293.     xelval maxval;
  294. {
  295.     int col, row, format;
  296.     xel *pnmrow = pnm_allocrow(head->xsize);
  297.     int sub = head->pixmin;
  298.  
  299.     if( head->zsize < 3 ) {
  300.         pm_message("writing PGM image");
  301.         format = PGM_TYPE;
  302.     }
  303.     else {
  304.         pm_message("writing PPM image");
  305.         format = PPM_TYPE;
  306.     }
  307.  
  308.     pnm_writepnminit(stdout, head->xsize, head->ysize, (xelval)maxval, format, 0);
  309.     for( row = head->ysize-1; row >= 0; row-- ) {
  310.         for( col = 0; col < head->xsize; col++ ) {
  311.             if( format == PGM_TYPE )
  312.                 PNM_ASSIGN1(pnmrow[col], image[row][col] - sub);
  313.             else {
  314.                 pixval r, g, b;
  315.                 r = image[row][col] - sub;
  316.                 g = image[head->ysize + row][col] - sub;
  317.                 b = image[2* head->ysize + row][col] - sub;
  318.                 PPM_ASSIGN(pnmrow[col], r, g, b);
  319.             }
  320.         }
  321.         pnm_writepnmrow(stdout, pnmrow, head->xsize, (xelval)maxval, format, 0);
  322.     }
  323.     pnm_freerow(pnmrow);
  324. }
  325.  
  326.  
  327. static void
  328. rle_decompress(src, srcleft, dest, destleft)
  329.     ScanElem *src;
  330.     int srcleft;
  331.     ScanElem *dest;
  332.     int destleft;
  333. {
  334.     int count;
  335.     unsigned char el;
  336.  
  337.     while( srcleft ) {
  338.         el = (unsigned char)(*src++ & 0xff);
  339.         --srcleft;
  340.         count = (int)(el & 0x7f);
  341.  
  342.         if( count == 0 )
  343.             return;
  344.         if( destleft < count )
  345.             pm_error("RLE error: too much input data (space left %d, need %d)", destleft, count);
  346.         destleft -= count;
  347.         if( el & 0x80 ) {
  348.             if( srcleft < count )
  349.                 pm_error("RLE error: not enough data for literal run (data left %d, need %d)", srcleft, count);
  350.             srcleft -= count;
  351.             while( count-- )
  352.                 *dest++ = *src++;
  353.         }
  354.         else {
  355.             if( srcleft == 0 )
  356.                 pm_error("RLE error: not enough data for replicate run");
  357.             while( count-- )
  358.                 *dest++ = *src;
  359.             ++src;
  360.             --srcleft;
  361.         }
  362.     }
  363.     pm_error("RLE error: no terminating 0-byte");
  364. }
  365.  
  366.  
  367. /* basic I/O functions, taken from ilbmtoppm.c */
  368.  
  369. static short
  370. get_big_short(ifp)
  371.     FILE *ifp;
  372. {
  373.     short s;
  374.  
  375.     if( pm_readbigshort(ifp, &s) == -1 )
  376.         readerr(ifp);
  377.  
  378.     return s;
  379. }
  380.  
  381. static long
  382. get_big_long(ifp)
  383.     FILE *ifp;
  384. {
  385.     long l;
  386.  
  387.     if( pm_readbiglong(ifp, &l) == -1 )
  388.         readerr(ifp);
  389.  
  390.     return l;
  391. }
  392.  
  393. static unsigned char
  394. get_byte(ifp)
  395.     FILE* ifp;
  396. {
  397.     int i;
  398.  
  399.     i = getc(ifp);
  400.     if( i == EOF )
  401.         readerr(ifp);
  402.  
  403.     return (unsigned char) i;
  404. }
  405.  
  406.  
  407. static void
  408. readerr(f)
  409.     FILE *f;
  410. {
  411.     if( ferror(f) )
  412.         pm_error("read error");
  413.     else
  414.         pm_error("premature EOF");
  415. }
  416.  
  417.  
  418. static void
  419. read_bytes(ifp, n, buf)
  420.     FILE *ifp;
  421.     int n;
  422.     char *buf;
  423. {
  424.     int r;
  425.  
  426.     r = fread((void *)buf, 1, n, ifp);
  427.     if( r != n )
  428.         readerr(ifp);
  429. }
  430.  
  431.  
  432. static short
  433. get_byte_as_short(ifp)
  434.     FILE *ifp;
  435. {
  436.     return (short)get_byte(ifp);
  437. }
  438.  
  439.  
  440. static void *
  441. xmalloc(bytes)
  442.     int bytes;
  443. {
  444.     void *mem;
  445.  
  446.     if( bytes == 0 )
  447.         return NULL;
  448.  
  449.     mem = malloc(bytes);
  450.     if( mem == NULL )
  451.         pm_error("out of memory allocating %d bytes", bytes);
  452.     return mem;
  453. }
  454.  
  455.  
  456. static char *
  457. compression_name(compr)
  458.     char compr;
  459. {
  460.     switch( compr ) {
  461.         case STORAGE_VERBATIM:
  462.             return "none";
  463.         case STORAGE_RLE:
  464.             return "RLE";
  465.         default:
  466.             return "unknown";
  467.     }
  468. }
  469.  
  470.